home *** CD-ROM | disk | FTP | other *** search
/ Aminet 16 / Aminet 16 (1996)(GTI - Schatztruhe)[!][Dec 1996].iso / Aminet / comm / bbs / Cit_Src_6J12.lha / vortex.c < prev    next >
C/C++ Source or Header  |  1996-09-07  |  17KB  |  530 lines

  1. /*
  2. *                               vortex.c
  3. *
  4. * Network "vortex" (aka infinite loop) handling code.
  5. */
  6. /*
  7. *                               history
  8. *
  9. * 88Oct23 HAW  Created.
  10. */
  11. #include "ctdl.h"
  12. #include "math.h"
  13. #include "vortex.h"
  14. /*
  15. *                               contents
  16. *
  17. *       InitVortexing()         allocate & initialize buffers
  18. *       NotVortex()             checks for vortex possibility
  19. */
  20. /*
  21. * Globals
  22. */
  23. extern char logNetResults;
  24. extern char netDebug;
  25. void *CheckVt();
  26. static void FreeVt(Vortex *d);
  27. /*
  28. * VortexList
  29. *
  30. * This is a list of systems found during a session of adding messages.  The
  31. * list is used for performance reasons.  When a session is over this list
  32. * is used to update our records on disk all at once..
  33. */
  34. SListBase VortexList =
  35.   {
  36.   NULL, CheckVt, NULL, FreeVt, NULL
  37.  
  38.   };
  39. char VortexHandle = TRUE;
  40. /*
  41. * External variable definitions for NET.C
  42. */
  43. extern CONFIG    cfg;   /* Lots an lots of variables    */
  44. extern MessageBuffer   msgBuf;
  45. extern rTable    *roomTab;
  46. extern NetBuffer netTemp, netBuf;
  47. extern FILE      *netLog;
  48. extern int       thisRoom;
  49. extern aRoom     roomBuf;       /* Room buffer  */
  50. /*
  51. * -Vortex Handling-
  52. *
  53. *    Vortex: the phenomenon of messages showing up on other systems more than
  54. * once.  Usually caused by a "loop" of backbones.
  55. *
  56. *    Vortexes can be caused by a number of problems, and the direct cause
  57. * of any particular vortex should not be important.  However, there is
  58. * a method available, as designed by CrT, to detect and nullify vortexes.
  59. *
  60. *    The method is to track the message numbers coming in for each system
  61. * sending messages.  Each message is supposed to carry the number it was
  62. * assigned on the originating system in one of the transmitted field.
  63. * Therefore, by remembering the highest message received from a given system,
  64. * we may identify and scuttle all messages which we believe we've already
  65. * received.
  66. *
  67. *    We must be aware that some shared rooms will be backboned, thus causing
  68. * messages not directly from the calling node to be sent to here.  We should
  69. * check these messages, too.
  70. *
  71. *    The central question is what level of granularity should be used to track
  72. * each system?  There are clearly two options:
  73. *
  74. * 1) Keep a single MSG_NUMBER for each system on the nodelist.  This has
  75. *    the attraction of little complexity -- the variable may be kept in
  76. *    the netTab & CTDLNET.SYS files, and will only take up 4 bytes per
  77. *    node.  However, this ignores the problem of interrupted net sessions:
  78. *    a room that was successfully transferred and processed can easily contain
  79. *    higher number messages than a room which was interrupted and
  80. *    re-transferred, thus causing incorrect vortex detection.  Interrupted
  81. *    sessions, while certainly not the rule, are not uncommon, thus making
  82. *    this objection reasonable.  Additionally, implementation of this option
  83. *    would require a Major Release, which is not appealing at the moment.
  84. *
  85. * 2) Keep a MSG_NUMBER for each room that each system nets with.  This option
  86. *    is clearly far more complex, but it does solve the major problem of
  87. *    option 1.  Furthermore, it should not require a Major Release in order
  88. *    to implement, which makes it more attractive.  Unfortunately, it may
  89. *    require some rewriting of the getNet code in order to support multiple
  90. *    netBuf variables, but that should not be utterly ghastly (or so we
  91. *    pray).
  92. *
  93. *    Neither option handles virtual rooms, nor are they intended to.  At the
  94. * moment, such support is not seen as warranted.
  95. *
  96. *    The initial implementation of net vortexing will be of option 2).  If, at
  97. * a later date, it becomes clear that 2) is unworkable or unneeded for some
  98. * reason, we can switch to Option 1) easily at the next Major Release point.
  99. *
  100. * IMPLEMENTATION
  101. *
  102. *    In order to avoid a Major Release, we cannot change the structure of
  103. * CTDLNET.SYS.  Furthermore, it is not attractive to attempt to handle an
  104. * arbitrary number of shared rooms per node within CTDLNET.SYS -- if we attempt
  105. * to handle the problem in the same manner as shared rooms and net archive
  106. * rooms (not implemented as of yet, sigh), we will run into limits, perhaps
  107. * far too often.
  108. *
  109. *    Therefore, we shall handle vortex records in the following way.
  110. *
  111. * 1) Each node on a system's nodelist may or may not have a file named
  112. *    "#.vtx", where "#" is the index into CTDLNET.SYS for that record.  These
  113. *    files will be created as needed, and thus some or many nodes (in
  114. *    particular disabled nodes) will not have corresponding .vtx files.  All
  115. *    .vtx files will be located in the #NETAREA directory.
  116. *
  117. * 2) Any .vtx file will be composed of 1 or more records of type VORTEX.
  118. *    Refer to CTDL.H for details, but each record is made up of an integer
  119. *    associated with roomTab, a GenNumber so we can detect when this record
  120. *    is no longer really in use, and the highest Message # received.
  121. *
  122. * 3) C-86 will keep an internal list of VORTEX records, one per node, during
  123. *    net processing.  As a room is processed, each message will force a
  124. *    #.vtx access when the originating system is occuring for the first
  125. *    time during this processing.  If the .vtx file contains a record for
  126. *    this room, then it is loaded into the internal list and can be referenced
  127. *    for the balance of this room's processing without going to disk.  If
  128. *    no record is found or the #.vtx file does not exist, appropriate creation
  129. *    takes place.  If any message is found to be less than the last highest
  130. *    received for this room, it is discarded.  [An audit would probably be
  131. *    desirable, too.]
  132. *
  133. *    Please note that no #.vtx files will be created or utilized if the option
  134. * has not been enabled for this installation.  Currently, this option is
  135. * enabled by placing "+vortex" on the CTDL command line.  Only systems with
  136. * excess disk capacity should use this capability.
  137. */
  138. /*
  139. 88Oct19 from Thom Brown @ Utica College, NY
  140. Hue - about the new code that detects (and presumably rejects) vortexed
  141. messages.  Would it be possible to have Citadel post a message to the Aide
  142. I have this fear that now that we don't get vortexed messages there will be no
  143. attempt for offending systems to clean up their configurations, so to speak.
  144. I suppose a parallel message out to go back to the offending system as well.
  145. */
  146. /*
  147. * 90July
  148. *
  149. * The scheme mentioned above is now being rewritten to function independent
  150. * of CtdlNet.Sys.  A database will be constructed as new systems are
  151. * detected.
  152. */
  153. #define getVortex(x, y)         (fread(y, sizeof *(y), 1, x) == 1)
  154. #define putVortex(x, y)         fwrite(y, sizeof *(y), 1, x)
  155.  
  156. static UNS_16 *VI;
  157. static UNS_16 TopVx = 0;
  158. extern char *READ_ANY, *APPEND_ANY;
  159. extern char *R_W_ANY, *WRITE_ANY;
  160. static int searchVortex(char *id, VtRecord *VtSystem);
  161. /*
  162. * VortexInit()
  163. *
  164. * This will initialize vortexing once and only once while an installation
  165. * is up.  It creates a dynamic table from the contents of vtxind.sys, an
  166. * index table.
  167. */
  168. void VortexInit()
  169.   {
  170.   FILE *fd;
  171.   SYS_FILE name;
  172.   long bytes;
  173.   if (!VortexHandle) return ;
  174.   makeSysName(name, "vtxind.sys", &cfg.netArea);
  175.   if ((fd = fopen(name, READ_ANY)) == NULL) VI = NULL;
  176.   else
  177.     {
  178.     totalBytes(&bytes, fd);
  179.     VI = GetDynamic((int) bytes);
  180.     if (fread(VI, (int) bytes, 1, fd) != 1)
  181.     VI = NULL;
  182.     else
  183.     TopVx = (int) bytes/sizeof(*VI);
  184.     fclose(fd);
  185.  
  186.     }
  187.   }
  188.  
  189. /*
  190. * InitVortexing()
  191. *
  192. * This is an initialization function which should be called before each
  193. * session of checking messages in from a network session.  Although at
  194. * the moment it does nothing, it and the call to it should be retained
  195. * in the interests of completeness and convenience in case something needs
  196. * to be added at a later date.
  197. */
  198. void InitVortexing()
  199.   {
  200.  
  201.  
  202.   }
  203. /*
  204. * NotVortex()
  205. *
  206. * This checks to see if the msg in msgBuf is vortexing or not.  FALSE is
  207. * returned if the message should be discarded.
  208. */
  209. char NotVortex()
  210.   {
  211.   int           NotUsed = -1, slot;
  212.   char  found, *low, IdAvailable = FALSE, DateAvailable = FALSE;
  213.   label       temp;
  214.   SYS_FILE    vortex;
  215.   FILE  *fd;
  216.   MSG_NUMBER  srcId = 0l;
  217.   long  srcDate = 0l;
  218.   extern char *READ_ANY;
  219.   Vortex      *Vtx;
  220.   VtRecord    VtSystem;
  221.   /* If no vortexing or we can't identify the nodeId ... */
  222.   if (!VortexHandle || !normId(msgBuf.mborig, temp))
  223.     {
  224.     splitF(NULL, " NotVortex:Not Vortexing or node id(%s) bad\n",msgBuf.mborig);
  225.     return TRUE;
  226.     };
  227.   /*
  228.   * If system not in vortexlist, then we must add it first before continuing
  229.   * onwards.
  230.   */
  231.   if ((Vtx = SearchList(&VortexList, temp)) == NULL)
  232.     {
  233.     if ((slot = searchVortex(temp, &VtSystem)) == ERROR)
  234.       {
  235.       if( logNetResults)splitF(netLog, "Msg from unmonitored node %s @%s, adding it.\n",
  236.       msgBuf.mboname, msgBuf.mborig);
  237.       /* adding new element to list */
  238.       if (TopVx == 0)
  239.       VI = malloc(++TopVx * sizeof *VI);
  240.       else
  241.       VI = realloc(VI, ++TopVx * sizeof *VI);
  242.       VI[TopVx - 1] = hash(temp);
  243.       makeSysName(vortex, "vtxind.sys", &cfg.netArea);
  244.       if ((fd = fopen(vortex, WRITE_ANY)) == NULL)
  245.         {
  246.         printf("Problems with vortex index file.\n");
  247.         return FALSE;
  248.  
  249.         }
  250.       fwrite(VI, 1, TopVx * sizeof *VI, fd);
  251.       fclose(fd);
  252.       strCpy(VtSystem.Id, temp);
  253.       strCpy(VtSystem.Name, msgBuf.mboname);
  254.       VtSystem.InUse = TRUE;
  255.       makeSysName(vortex, "vortex.sys", &cfg.netArea);
  256.       if ((fd = fopen(vortex, (TopVx == 1) ? WRITE_ANY : APPEND_ANY))
  257.       == NULL)
  258.         {
  259.         printf("Problems with vortex file.\n");
  260.         return FALSE;
  261.  
  262.         }
  263.       fwrite(&VtSystem, 1, sizeof VtSystem, fd);
  264.       fclose(fd);
  265.       slot = TopVx - 1;
  266.  
  267.       }
  268.     else   if (cfg.BoolFlags.debug)splitF(NULL, "searchVortex:found\n");
  269.  
  270.     }
  271.   else slot = Vtx->SystemNum;
  272.   if (Vtx == NULL)
  273.     {
  274.     /*
  275.     * Check to see if this file exists
  276.     */
  277.     Vtx = GetDynamic(sizeof *Vtx);
  278.     AddData(&VortexList, Vtx, NULL, FALSE);
  279.     strCpy(Vtx->System, temp);
  280.     Vtx->SystemNum = slot;
  281.     sPrintf(temp, "%d.vex", slot);
  282.     makeSysName(vortex, temp, &cfg.netArea);
  283.     if ((fd = safeopen(vortex, READ_ANY)) != NULL)
  284.       {
  285.       /*
  286.       * File is here, so search for a record corresponding to this room.
  287.       */
  288.       Vtx->Slot = 0;
  289.       while ((found = getVortex(fd, &Vtx->data)))
  290.         {
  291.         /* First, keep an eye out for recycling */
  292.         if (Vtx->data.vGen !=
  293.         roomTab[Vtx->data.vRoomNo].rtgen)
  294.         NotUsed = Vtx->Slot;      /* mark it */
  295.         else if (Vtx->data.vRoomNo == thisRoom)
  296.         break;      /* Found it, so stop here */
  297.         Vtx->Slot++;
  298.  
  299.         }
  300.       if (!found)
  301.         {
  302.         /* Garbage collection measure */
  303.         if (NotUsed != -1)
  304.         Vtx->Slot = NotUsed;
  305.  
  306.         }
  307.       fclose(fd);
  308.  
  309.       }
  310.     if (fd == NULL)
  311.     Vtx->Slot = 0;
  312.     if (fd == NULL || !found)
  313.       {
  314.       Vtx->data.vRoomNo = thisRoom;
  315.       Vtx->data.vGen = roomTab[thisRoom].rtgen;
  316.       /* this shouldn't hurt */
  317.       Vtx->data.vHighest = 1l;
  318.  
  319.       }
  320.  
  321.     }
  322.   /*-Construct srcId number here-*/
  323.   if (strLen(msgBuf.mbsrcId) != 0)
  324.     {
  325.     IdAvailable = TRUE;
  326.     if ((low = strchr(msgBuf.mbsrcId, ' ')) == NULL)
  327.     srcId = atol(msgBuf.mbsrcId);
  328.     else
  329.     srcId = (atol(msgBuf.mbsrcId) << 16) + atol(low + 1);
  330.  
  331.     }
  332.   if (strLen(msgBuf.mbdate) != 0 || strLen(msgBuf.mbtime) != 0)
  333.   if ((DateAvailable = ReadDate(msgBuf.mbdate, &srcDate)))
  334.   srcDate += ReadTime(msgBuf.mbtime);
  335.   /*
  336.   * Comparison note:
  337.   * The current scheme involves both the date of the message and the
  338.   * source ID of the message.  This gives us security against both
  339.   * incorrect system dates and message base resetting.  Unfortunately,
  340.   * STadel does not transmit source IDs.  Therefore, the rules for
  341.   * comparison are a little obscure.  Basically, the following if
  342.   * boils down to this:
  343.   *  o if the source id is missing, compare against the latest date
  344.   *    of a message received in this room for the source system of
  345.   *    this message.  The comparison is NON-inclusive (i.e., strictly
  346.   *    less than), because it is conceivable for two net messages to
  347.   *    contain the same date and time -- message composition is not
  348.   *    always a lengthy process.  Note this leaves a "hole" in the
  349.   *    vortex detection department -- we don't dare test non-strictly,
  350.   *    since that can result in losing net messages, but this lets
  351.   *    certain vortexed messages get through.
  352.   *  o if both date and source id are available, then the message
  353.   *    must fail both tests before it is rejected.  That's our
  354.   *    fail-safe mentioned earlier.  During this* comparison, we
  355.   *    use an inclusive (non-strict) comparison of message dates,
  356.   *    because the message # check will take care of distinguishing
  357.   *    between two messages written under the same date/time stamp.
  358.   *    Therefore, there's no hole for us to worry about.  That
  359.   *    difference is what makes this if so scary looking.
  360.   */
  361.   if ((!IdAvailable || srcId <= Vtx->data.vHighest) &&
  362.   (!DateAvailable || (IdAvailable && srcDate <= Vtx->data.vLastDate) ||
  363.   (!IdAvailable && srcDate < Vtx->data.vLastDate)))
  364.     {
  365.     if( logNetResults && netDebug)splitF(netLog, "%s from %s rejected.\n", msgBuf.mbsrcId,
  366.     msgBuf.mboname);
  367.     Vtx->Detect = TRUE;
  368.     return FALSE;
  369.  
  370.     }
  371.   else
  372.     {
  373.     if (IdAvailable)
  374.     Vtx->Current = max(Vtx->Current, srcId);
  375.     if (DateAvailable)
  376.     Vtx->DateCurrent = max(Vtx->DateCurrent, srcDate);
  377.     return TRUE;
  378.  
  379.     }
  380.  
  381.   }
  382. static int errors;
  383. /*
  384. * FinVortexing()
  385. *
  386. * This function should be called to finish a vortex checking session.  This
  387. * saves updates to disk.
  388. */
  389. void FinVortexing()
  390.   {
  391.   if (!VortexHandle)
  392.   return;
  393.   sPrintf(msgBuf.mbtext, "Vortex attempt by %s in %s, involving system(s) ",
  394.   netBuf.netName, roomBuf.rbname);
  395.   errors = 0;
  396.   KillList(&VortexList);
  397.   if (errors)
  398.     {
  399.     strCat(msgBuf.mbtext, ".");
  400.     netResult(msgBuf.mbtext);
  401.  
  402.     }
  403.  
  404.   }
  405. /*
  406. * FreeVt()
  407. *
  408. * This will write and free an element of the vortex list; obviously, it is
  409. * used in list handling.
  410. */
  411. static void FreeVt(Vortex *d)
  412.   {
  413.   label    temp;
  414.   SYS_FILE vortex;
  415.   FILE     *fd;
  416.   VtRecord VtSystem;
  417.   if (d->Current > d->data.vHighest)
  418.   d->data.vHighest = d->Current;
  419.   if (d->DateCurrent > d->data.vLastDate)
  420.   d->data.vLastDate = d->DateCurrent;
  421.   sPrintf(temp, "%d.vex", d->SystemNum);
  422.   makeSysName(vortex, temp, &cfg.netArea);
  423.   if ((fd = safeopen(vortex, R_W_ANY)) == NULL)
  424.     {
  425.     if ((fd = safeopen(vortex, WRITE_ANY)) == NULL)
  426.       {
  427.       if( logNetResults && netDebug) splitF(netLog, "Couldn't create %s!!\n", vortex);
  428.  
  429.       }
  430.  
  431.     }
  432.   else
  433.     {
  434.     fseek(fd, d->Slot * sizeof d->data, 0);
  435.  
  436.     }
  437.   if (fd != NULL)
  438.     {
  439.     putVortex(fd, &d->data);
  440.     fclose(fd);
  441.  
  442.     }
  443.   if (d->Detect)
  444.     {
  445.     errors++;
  446.     if (searchVortex(d->System, &VtSystem) != ERROR)
  447.     sPrintf(lbyte(msgBuf.mbtext), (errors == 1) ? "%s" : ", %s",
  448.     VtSystem.Name);
  449.  
  450.     }
  451.   free(d);
  452.  
  453.   }
  454. /*
  455. * CheckVt()
  456. *
  457. * This function is used to search the list of vortex records for a given
  458. * system.
  459. */
  460. void *CheckVt(Vortex *d, char *s)
  461.   {
  462.   return (strCmpU(d->System, s) == SAMESTRING) ? d : NULL;
  463.  
  464.   }
  465. /*
  466. * ReadTime()
  467. *
  468. * This function reads the time stamp of a message and returns a long
  469. * integer indicating the absolute time of the message in seconds past
  470. * midnight.  It does NOT handle time zones.
  471. */
  472. long ReadTime(char *time)
  473.   {
  474.   char *s, afternoon;
  475.   long ret;
  476.   if (strLen(time) == 0) return 0l;
  477.   if ((s = strrchr(time, ' ')) == NULL) return 0l;
  478.   if (strCmpU(s + 1, "am") == SAMESTRING) afternoon = FALSE;
  479.   else if (strCmpU(s + 1, "pm") == SAMESTRING) afternoon = TRUE;
  480.   else return 0l;
  481.   if ((s = strchr(time, ':')) == NULL) return 0l;
  482.   ret = atol(time) * 60l;
  483.   if (afternoon)
  484.     {
  485.     if (atol(time) != 12) ret += 720l;
  486.  
  487.     }
  488.   else if (atol(time) == 12) ret = 0l;
  489.   ret += atol(s + 1);
  490.   return 60 * ret;
  491.  
  492.   }
  493. /*
  494. * searchVortex()
  495. *
  496. * This function will search the vortex list for the given system.  It
  497. * return ERROR if the system is not in the database, otherwise an index
  498. * into it in terms of records.
  499. */
  500. static int searchVortex(char *id, VtRecord *VtSystem)
  501.   {
  502.   int      rover;
  503.   UNS_16   hval;
  504.   FILE     *fd;
  505.   SYS_FILE name;
  506.   makeSysName(name, "vortex.sys", &cfg.netArea);
  507.   if ((fd = fopen(name, READ_ANY)) == NULL) return ERROR;
  508.   hval = hash(id);
  509.   for (rover = 0; rover < TopVx; rover++)
  510.     {
  511.     if (VI[rover] == hval)
  512.       {
  513.       if (fd == NULL)
  514.       if ((fd = fopen(name, READ_ANY)) == NULL) return ERROR;
  515.       fseek(fd, rover * sizeof *VtSystem, 0);
  516.       if (fread(VtSystem, sizeof *VtSystem, 1, fd) > 0)
  517.         {
  518.         if (strCmpU(id, VtSystem->Id) == SAMESTRING) break;
  519.  
  520.         }
  521.  
  522.       }
  523.  
  524.     }
  525.   if (fd != NULL)
  526.   fclose(fd);
  527.   return (rover == TopVx) ? ERROR : rover;
  528.  
  529.   }
  530.